/* Copyright (c) 2011 Jordan Gray, MIT; see COPYRIGHT */

#include "lint.h"

#include <algorithm>
#include <iostream>
#include <assert.h>
#include "lint_rep.h"

namespace ch11ex05 {

LINT::LINT() : p_rep_(new LINT::LINTRep()) {}
LINT::LINT(const std::list<unsigned char>& digits, bool negative) :
    p_rep_(new LINT::LINTRep(digits, negative)) {}

LINT::LINT(const LINT& lint) : p_rep_(lint.p_rep_) {}

LINT::LINT(int val) {
  // compute sign
  bool negative = false;
  if(val < 0) {
    negative = true;
    val = -val;
  }
  // compute digits
  std::list<unsigned char> digits;
  if(val == 0) digits.push_front(0);
  else {
    while(val != 0) {
      digits.push_front(val % 10);
      val /= 10;
    }
  }
  p_rep_.reset(new LINT::LINTRep(digits, negative));
}

LINT& LINT::operator=(LINT lint) { std::swap(p_rep_, lint.p_rep_); return *this; }

bool LINT::operator==(const LINT& lint) const {
  return LINT::LINTRep::IsEqual(*p_rep_, *lint.p_rep_);
}
bool LINT::operator!=(const LINT& lint) const { return !operator==(lint); }

LINT& LINT::ComputeArith(char operation, const LINT& operand) {
  MakeUnique();
  switch(operation) {
    case '+':
      p_rep_->SignedAccumulate(*operand.p_rep_, false);
      break;
    case '-':
      p_rep_->SignedAccumulate(*operand.p_rep_, true);
      break;
    case '*':
      p_rep_->SignedMultiply(*operand.p_rep_);
      break;
    case '/':
      p_rep_->SignedDivide(*operand.p_rep_);
      break;
    default:
      assert(false);
  }
  return *this;
}

LINT& LINT::operator+=(const LINT& lint) { return ComputeArith('+',lint); }
LINT& LINT::operator-=(const LINT& lint) { return ComputeArith('-',lint); }
LINT& LINT::operator*=(const LINT& lint) { return ComputeArith('*',lint); }
LINT& LINT::operator/=(const LINT& lint) { return ComputeArith('/',lint); }
LINT LINT::operator+(const LINT& lint) const { return LINT(*this).operator+=(lint); }
LINT LINT::operator-(const LINT& lint) const { return LINT(*this).operator-=(lint); }
LINT LINT::operator*(const LINT& lint) const { return LINT(*this).operator*=(lint); }
LINT LINT::operator/(const LINT& lint) const { return LINT(*this).operator/=(lint); }

LINT& LINT::MakeUnique() {
  p_rep_.reset(new LINT::LINTRep(*p_rep_));
  return *this;
}
//LINT::LINT(ShPtr_LINTRep p_rep) : p_rep_(p_rep) {}

std::ostream& operator<<(std::ostream& out, const LINT& lint) {
  out << lint.p_rep_->ToString();
  return out;
}

} // namespace
